package day_01_SaleTicket;

/**
 * 函数式接口：
 *  1、当前接口内，有且仅有一个方法声明.
 *  2、@FunctionalInterface 注解，就是用来标注当前接口是一个函数式接口的，即内部有且仅能有一个方法声明；
 *  3、要想使用 lambda 表达式，那么必须得保证 接口是一个 函数式接口！
 */
@FunctionalInterface
interface Foo{

    //public void sayHello();

    public int add(int x,int y);

    /**
     * 关于接口中的方法实现：
     * 1、在 java8 之前，接口中只允许有方法的声明，不能有方法的实现；
     * 2、java8 之后，接口中允许有 default 形式的方法实现；
     * 如何理解 java8 之后，接口中允许有方法实现，且只允许有 default 形式的方法实现：
     * 1、当前接口，是一个函数式接口，有且仅有一个方法的声明，注意是方法声明；
     * 2、但是 default 形式的方法实现，不作为函数式接口的检查内容，也就是说接口内无论有多少个 default 形式的方法实现都不影响；
     * 3、default 形式的方法实现，在接口内部是可以有多个的；
     * 4、default 形式的方法实现，该方法是可以被实现类重写的！
     * 接口的静态方法实现，也不在函数式接口的检查范围内；
     * 且接口的静态方法实现不能被重写！
     */
    default int div(int x,int y){
        System.out.println("java8 之后，接口中允许有 default 形式的方法实现！");
        return x / y;
    }

    default int mul(int a,int b){
        System.out.println("java8 之后，接口中允许有 default 形式的方法实现！");
        return a * b;
    }

    /**
     * 接口中的静态方法实现
     * @param x
     * @return
     */
    public static int abs(int x){
        return x >= 0 ? x : -x;
    }

    public static int sqrt(int x){
        return Integer.parseInt(Math.sqrt(Double.parseDouble(x + "")) + "");
    }

}

/**
 * 1、default 方法实现也是可以被重写的！
 * 2、接口的静态方法实现，是不可以被重写的！（所以接口的静态方法实现，只能通过 接口名.静态方法实现的方法名 来调用）
 */
class FooImpl implements Foo{

    @Override
    public int add(int x, int y) {
        return 0;
    }

    @Override
    public int div(int x, int y) {
        System.out.println("重写接口中 default 形式的方法实现！");
        return 0;
    }

    @Override
    public int mul(int a, int b) {
        return 0;
    }
}


/**
 * @Description lambda 表达式学习
 * @Author shihan
 * @Date 2020/10/25 15:17
 * @Version 1.0
 */
public class LambdaExpress {

    public static void main(String[] args) {
        //一、回顾一下匿名内部类
        /*
        Foo foo = new Foo() {
            @Override
            public void sayHello() {
                System.out.println("匿名内部类，sayHello....");
            }
        };
        foo.sayHello();
        */

        //二、然后使用 lambda 表达式
        /**
         * lambda 表达式，只适合用在函数式接口中（即接口内部，有且只有一个方法声明的接口）；
         * 且 lambda 表达式是匿名内部类方式的简化写法；
         * 匿名内部类（最终返回一个接口的实现类对象）：new 接口名() { 重写的方法头以及方法体的实现代码 }
         * lambda表达式（最终也返回一个接口的实现类对象）：() -> { 方法体的实现代码 }
         * lambda表达式方式，效果和匿名内部类效果是一样的，但是可以简化代码，使得代码更简洁明了！
         * 最终只需要保留 方法参数列表、方法体代码 即可！
         */
        /*
        Foo foo1 = () -> {
            System.out.println("lambda 表达式，sayHello....");
        };
        foo1.sayHello();
        */

        //=================================================================

        /**
         * 当函数式接口中的方法声明，是带参数的，并且是有返回值的：
         * 1、lambda 表达式中的参数列表个数、顺序要对应，即小括号内的参数列表;
         * 2、且可以省略参数的变量类型（因为在接口中已明确指明）；
         * 3、再次强调，lambda 表达式等效于匿名内部类，返回的是一个接口的实现类对象；
         * 4、得到接口的实现类对象后，返回值通过方法调用进行接收即可！
         */
        Foo foo = (x,y) -> {
            return x + y;
        };
        System.out.println(foo.add(3, 4));

        /**
         * 1、default 的方法实现，不作为函数式接口的校验内容；
         * 2、接口内可以有多个 default 形式的方法实现；
         * 3、接口中 default 形式的方法实现，也是可以被重写的！
         */
        System.out.println(new FooImpl().div(2, 1));

        /**
         * 接口的静态方法实现，只能通过 接口名.静态方法实现 来调用；
         * 拿实现了接口的实现类 或者 接口的实现类对象，都无法调用 接口的静态方法实现；
         * foo.abs(-5);
         * FooImpl.abs(-5);
         * 这两种方式都无法成功调用，接口的静态方法实现！
         */
        System.out.println(Foo.abs(-5));


    }
}

